--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Commit 35d07e569b19752b7582c95afc53677a6199980a
Parents : 935cee6
Author : Ivan <ivan@quad4.io>
Signature : Signature validation error
Date : 2026-05-02T15:46:34-05:00
feat(parser): add word wrapping and space splitting functionality to MicronParser
Changes
2 files changed, 83 insertions(+), 0 deletions(-)
Diff
diff --git a/meshchatx/src/frontend/js/MicronParser.js b/meshchatx/src/frontend/js/MicronParser.js
index 33871017..9acd5302 100644
--- a/meshchatx/src/frontend/js/MicronParser.js
+++ b/meshchatx/src/frontend/js/MicronParser.js
@@ -460,6 +460,38 @@ export default class MicronParser extends BaseMicronParser {
return super.parseLine(line, state);
}
+ wrapWord(word) {
+ if (word.length === 0) return "";
+ if (!MicronParser.lineNeedsPerCharCells(word)) {
+ return "<span class='Mu-mnt-group'>" + escapeHtmlForFallback(word) + "</span>";
+ }
+ let out = "";
+ let charArr;
+ try {
+ charArr = [...new Intl.Segmenter().segment(word)].map((x) => x.segment);
+ } catch {
+ try {
+ charArr = Array.from(word);
+ } catch {
+ charArr = word.split("");
+ }
+ }
+ for (let char of charArr) {
+ const cellClass = MicronParser.isWideMonospaceCell(char) ? "Mu-mnt-full" : "Mu-mnt";
+ out += "<span class='" + cellClass + "'>" + escapeHtmlForFallback(char) + "</span>";
+ }
+ return "<span class='Mu-mws'>" + out + "</span>";
+ }
+
+ splitAtSpaces(line) {
+ let out = "";
+ const wordArr = line.split(/(?<= )/g);
+ for (const word of wordArr) {
+ out += this.wrapWord(word);
+ }
+ return out;
+ }
+
forceMonospace(line) {
if (line == null || line === "") {
return "";
diff --git a/tests/frontend/MicronParser.test.js b/tests/frontend/MicronParser.test.js
index 4bcb0d0a..04a88220 100644
--- a/tests/frontend/MicronParser.test.js
+++ b/tests/frontend/MicronParser.test.js
@@ -98,6 +98,57 @@ describe("MicronParser.js", () => {
expect(html).toContain("background-color: rgb(153, 153, 153)"); // #999
});
+ it("handles bare headings without adding extra lines", () => {
+ // Bare headings are headings without content after them on the same line
+ // They should not insert extra blank lines
+ const markup = `>Anonymous Git Node
+
+>>
+\`[Node\`:/page/index.mu] / \`[public\`:/page/group.mu\`g=public] / LXMF
+
+>LXMF - \`*A simple messaging format\`*
+>>
+
+\`[Files\`:/page/tree.mu] \`[Commits\`:/page/commits.mu]`;
+ const html = parser.convertMicronToHtml(markup);
+
+ // All content should be present
+ expect(html).toContain("Anonymous Git Node");
+ expect(html).toContain("Node");
+ expect(html).toContain("public");
+ expect(html).toContain("LXMF");
+ expect(html).toContain("A simple messaging format");
+ expect(html).toContain("Files");
+ expect(html).toContain("Commits");
+
+ // Verify no consecutive <br><br> patterns that would indicate extra lines
+ // from bare headings (normalize HTML first)
+ const normalized = html.replace(/>\s+</g, "><");
+ expect(normalized).not.toContain("<br><br><br>");
+ });
+
+ it("handles nested bare headings with section indents", () => {
+ // Multiple nested bare headings should apply section indentation correctly
+ const markup = `>Section A
+>>>
+Content at depth 3
+
+>>Section B
+>
+Content at depth 1`;
+ const html = parser.convertMicronToHtml(markup);
+
+ expect(html).toContain("Section A");
+ expect(html).toContain("Section B");
+ expect(html).toContain("Content at depth 3");
+ expect(html).toContain("Content at depth 1");
+
+ // Check for section indentation via inline margin styles (2.4em = 2 levels * 1.2em per level)
+ expect(html).toContain("margin-left: 2.4em");
+ // Content after Section B should be at depth 1 (1.2em margin)
+ expect(html).toContain("margin-left: 1.2em");
+ });
+
it("converts horizontal dividers", () => {
const markup = "-";
const html = parser.convertMicronToHtml(markup);
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────